﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Unity.Profiling.Editor;
using UnityEditor;
using UnityEditor.UIElements;
using UnityEditorInternal;
using UnityEngine;
using UnityEngine.UIElements;

namespace SGEngine.ProfilerEx
{
    public class ProfilerGCViewController : ProfilerModuleViewController
    {
        public static MultiOptionSelectWindow.OptionItem[] s_SelectOptions = new[]
        {
            new MultiOptionSelectWindow.OptionItem("Main Thread", true),
            new MultiOptionSelectWindow.OptionItem("Render Thread", false),
            new MultiOptionSelectWindow.OptionItem("Scripting Threads", true),
            new MultiOptionSelectWindow.OptionItem("Other Threads", false),
            new MultiOptionSelectWindow.OptionItem("Loading", true),
            new MultiOptionSelectWindow.OptionItem("Audio", false),
            new MultiOptionSelectWindow.OptionItem("Profiler", false),
            new MultiOptionSelectWindow.OptionItem("AssetDatabase", false),
            new MultiOptionSelectWindow.OptionItem("BakingJobs", false),
            new MultiOptionSelectWindow.OptionItem("Job", false),
            new MultiOptionSelectWindow.OptionItem("CloudJob", false),
        };

        public readonly static Color s_BtnBackgourdColor = new Color(226, 223, 223);
        
        private static int s_Id;
        private bool m_IsRevertTree;
        private string m_SortColumnName;
        private bool m_SortAcsending;

        private Label m_TxtTreads;
        private Toggle m_TogReverse;
        private MultiColumnTreeView m_TreeView;
        
        public ProfilerGCViewController(ProfilerWindow profilerWindow) : base(profilerWindow)
        {
            ProfilerExData.Instance.AddEventHandler(ProfilerExData.k_EventDataChange, OnEventDataChange);
            ProfilerDriver.NewProfilerFrameRecorded += OnNewProfilerFrameRecorded;
            ProfilerDriver.profileLoaded += OnProfileLoaded;
        }

        protected override VisualElement CreateView()
        {
            var rootVisualElement = new VisualElement();
            var uxml = Resources.Load<VisualTreeAsset>("editor_resources/ProfileGcAnalyzeView");
            uxml.CloneTree(rootVisualElement);
            
            m_TreeView = rootVisualElement.Q<MultiColumnTreeView>();
            // For each column, set Column.makeCell to initialize each node in the tree.
            // You can index the columns array with names or numerical indices.
            m_TreeView.columns["name"].makeCell = () => new Label();
            m_TreeView.columns["gc"].makeCell = () => new Label();
            m_TreeView.columns["callcount"].makeCell = () => new Label();
            // For each column, set Column.bindCell to bind an initialized node to a data item.
            m_TreeView.columns["name"].bindCell = (VisualElement element, int index) =>
                (element as Label).text = m_TreeView.GetItemDataForIndex<ProfilerTreeNode>(index).Name;
            m_TreeView.columns["callcount"].bindCell = (VisualElement element, int index) =>
                (element as Label).text = m_TreeView.GetItemDataForIndex<ProfilerTreeNode>(index).CallCount.ToString();
            m_TreeView.columns["gc"].bindCell = (VisualElement element, int index) =>
                (element as Label).text = m_TreeView.GetItemDataForIndex<ProfilerTreeNode>(index).GcDesc;
            m_TreeView.columns["selfgc"].bindCell = (VisualElement element, int index) =>
                (element as Label).text = m_TreeView.GetItemDataForIndex<ProfilerTreeNode>(index).SelfGcDesc;
            m_TreeView.columns["timems"].bindCell = (VisualElement element, int index) =>
                (element as Label).text = m_TreeView.GetItemDataForIndex<ProfilerTreeNode>(index).TimeMsDesc;
            m_TreeView.columns["selfms"].bindCell = (VisualElement element, int index) =>
                (element as Label).text = m_TreeView.GetItemDataForIndex<ProfilerTreeNode>(index).SelfMsDesc;
            
            m_TxtTreads = rootVisualElement.Q<Label>();
            m_TxtTreads.text = GetThreadSelectedMsg();
            var btnLoad = rootVisualElement.Q<ToolbarButton>("btnSelectThread");
            btnLoad.RegisterCallback<MouseUpEvent>(OnBtnSelectThreadClick);
            m_TogReverse = rootVisualElement.Q<Toggle>("togReverse");
            m_TogReverse.RegisterValueChangedCallback(OnTogReveserTreeChange);

            var btnImport = rootVisualElement.Q<ToolbarButton>("btnImport");
            btnImport.RegisterCallback<MouseUpEvent>(OnBtnImportClick);
            
            var btnSave = rootVisualElement.Q<ToolbarButton>("btnSave");
            btnSave.RegisterCallback<MouseUpEvent>(OnBtnSaveClick);
            
            var btnPullProfile = rootVisualElement.Q<ToolbarButton>("btnPullProfile");
            btnPullProfile.RegisterCallback<MouseUpEvent>(OnBtnPullProfileClick);
            
            m_TreeView.columnSortingChanged += OnColumnSortingChanged;
            RefreshTreeView();
            return rootVisualElement;
        }

        protected override void Dispose(bool disposing)
        {
            ProfilerExData.Instance.RemoveEventHandler(ProfilerExData.k_EventDataChange, OnEventDataChange);
            base.Dispose(disposing);
        }

        private async Task RefreshTreeView()
        {
            ProfilerExData.Instance.ProfilerTreeData.MakeSelfGc();
            var treeData =  ProfilerExData.Instance.ProfilerTreeData;
            if (m_IsRevertTree)
            {
                using (var counter = new EditorTimeCounter("revert tree async"))
                {
                    treeData = await ProfilerTreeData.ReverseTreeAsync(ProfilerExData.Instance.ProfilerTreeData);    
                    // treeData = ProfilerTreeData.ReverseTree(ProfilerExData.Instance.ProfilerTreeData);    
                }
            }

            if (m_SortColumnName != null)
            {
                treeData.Root.SortChildren(m_SortColumnName, m_SortAcsending);
            }
            var itemDataList = SetUpTreeViewItemData(treeData);
            m_TreeView.SetRootItems(itemDataList);
            m_TreeView.Rebuild();
        }
        
        private List<TreeViewItemData<ProfilerTreeNode>> SetUpTreeViewItemData(ProfilerTreeData tree)
        {
            s_Id = 0;
            var result = new List<TreeViewItemData<ProfilerTreeNode>>();
            foreach (var childNode in tree.Root.Children)
            {
                var childItem = ConvertToTreeViewItemData(childNode);
                result.Add(childItem);
            }
            return result;
        }

        private TreeViewItemData<ProfilerTreeNode> ConvertToTreeViewItemData(ProfilerTreeNode node)
        {
            var id = s_Id++;
            var childrenList = new List<TreeViewItemData<ProfilerTreeNode>>();
            foreach (var childNode in node.Children)
            {
                var childItem = ConvertToTreeViewItemData(childNode);
                childrenList.Add(childItem);
            }
            var itemData = new TreeViewItemData<ProfilerTreeNode>(id, node, childrenList);
            return itemData;
        }

        private string GetThreadSelectedMsg()
        {
            var OptionsList = new List<string>();
            foreach (var option in s_SelectOptions)
            {
                if (option.IsSelect)
                {
                    OptionsList.Add(option.Msg);
                }
            }
            return string.Join(", ", OptionsList);
        }

        #region 事件
        private void OnEventDataChange(params object[] args)
        {
            RefreshTreeView();
        }

        private void OnProfileLoaded()
        {
            m_TogReverse.value = false;
            m_IsRevertTree = false;
        }

        private void OnNewProfilerFrameRecorded(int connectionId, int newFrameIndex)
        {
            m_TogReverse.value = false;
            m_IsRevertTree = false;
        }

        private void OnColumnSortingChanged()
        {
            var item = m_TreeView.sortedColumns.FirstOrDefault();
            m_SortColumnName = item.columnName;
            m_SortAcsending = item.direction == SortDirection.Ascending;
            RefreshTreeView();
        }

        private void OnBtnSelectThreadClick(MouseUpEvent e)
        {
            MultiOptionSelectWindow.ShowWindow("选择监控线程", ref s_SelectOptions, OnOptionSelectedCallback);
        }
        
        private void OnBtnImportClick(MouseUpEvent e)
        {
            string filePath = EditorUtility.OpenFilePanel("选择导入文件路径", "", "gcdata");
            ProfilerExData.Instance.LoadFromLocal(filePath);
        }
        private void OnBtnSaveClick(MouseUpEvent e)
        {
            string filePath = EditorUtility.SaveFilePanel("选择保存路径", "", $"GcData_{DateTime.Now.ToString("yyyyMMddHHmmss")}", "gcdata");
            ProfilerExData.Instance.SaveToLocal(filePath);
        }
        private void OnBtnPullProfileClick(MouseUpEvent e)
        {
            ProfilerExData.Instance.LoadDataByProfilerData();
        }

        private void OnOptionSelectedCallback()
        {
            m_TxtTreads.text = GetThreadSelectedMsg();
        }

        private void OnTogReveserTreeChange(ChangeEvent<bool> arg)
        {
            m_IsRevertTree = arg.newValue;
            RefreshTreeView();
        }
        #endregion
    }
}